<template>
  <div class="m-upload">
    <el-upload
      ref="uploadRef"
      v-bind="$attrs"
      :multiple="props.multiple"
      :accept="props.accept"
      :list-type="props.listType"
      :disabled="disabled"
      :file-list="fileList"
      :on-preview="onPreview"
      :on-remove="onRemove"
      :on-change="onChange"
      :on-exceed="handleExceed"
      :before-upload="beforeUpload"
      :http-request="httpRequest"
    >
      <slot :disabled="disabled">
        <div v-if="!$attrs.drag">
          <el-button
            v-if="props.listType !== 'picture-card'"
            :disabled="disabled"
            type="primary"
            >上传文件</el-button
          >
          <m-icon v-else name="icon-plus"></m-icon>
        </div>
        <div v-else>
          <div>
            <img style="width: 30px" src="@/assets/imgs/u897.png" />
          </div>
          <div class="el-upload__text">点击或拖拽上传文件</div>
          <div style="color: #8e8e8e">
            文件不能超过{{ props.maxSize }}MB，支持{{
              props.accept.split(',').join('、')
            }}格式
          </div>
        </div>
      </slot>
      <template #tip>
        <slot name="tip"></slot>
      </template>
    </el-upload>
  </div>
</template>

<script setup lang="ts">
import type {
  UploadProps as ElUploadProps,
  UploadInstance,
  UploadRawFile,
  UploadProps
} from 'element-plus'
import type { UploadFile, UploadRule } from './upload'
import { uploadProps, uploadEmits } from './upload'
import { ref, watch, computed, reactive } from 'vue'
import { ElMessage, genFileId } from 'element-plus'
import { api as viewerApi } from 'v-viewer'
import { useProgressFake } from '@/hooks'
import { cloneDeep, joinBaseUrlFile, isImageByExtname } from '@/utils'

defineOptions({
  name: 'MUpload',
  inheritAttrs: false
})

const props = defineProps(uploadProps)
const emit = defineEmits(uploadEmits)

const fileList = ref<UploadFile[]>([])
const uploadRef = ref<UploadInstance>()

watch(
  () => props.modelValue,
  (val) => {
    if (fileList.value === val) {
      return
    }
    const modelValue = cloneDeep(val)
    fileList.value = modelValue?.map((item) => ({
      ...item,
      url: joinBaseUrlFile(item.url),
      response: {
        filename: item.name,
        url: item.url,
        ...item.response
      }
    }))
  },
  {
    immediate: true,
    deep: true
  }
)

watch(
  () => fileList.value,
  (val) => {
    emit('update:modelValue', val)
    emit('change', val)
  },
  {
    immediate: true,
    deep: true
  }
)

const imageFileList = computed(() =>
  fileList.value.filter(
    ({ url, name }) => isImageByExtname(name) || isImageByExtname(url)
  )
)

const onPreview: ElUploadProps['onPreview'] = (uploadFile) => {
  if (!props.preview) {
    return
  }

  // onPreview
  if (props.preview !== true) {
    props.preview(uploadFile as UploadFile)
    return
  }

  const { url, name } = uploadFile
  if (!url) {
    return
  }

  if (!(isImageByExtname(name) || isImageByExtname(url))) {
    return window.open(url)
  }

  const initialViewIndex = imageFileList.value.findIndex(
    (file) => file.uid === uploadFile.uid
  )

  viewerApi({
    // TODO: https://github.com/fengyuanchen/viewerjs
    options: {
      toolbar: true,
      url: 'url',
      initialViewIndex
    },
    images: imageFileList.value
  })
}

const onRemove: ElUploadProps['onRemove'] = (uploadFile, uploadFiles) => {
  fileList.value = uploadFiles as UploadFile[]
  emit('onRemove', uploadFile)
}

const isMax = computed(() => fileList.value.length >= props.max)
const disabled = computed(() => props.disabled)

const beforeUpload: ElUploadProps['beforeUpload'] = (rawFile) => {
  const rules: UploadRule[] = [
    {
      validator: () => isMax.value,
      message: `最多上传${props.max}个文件`
    },
    {
      validator: (file) => {
        if (props.accept === 'all') {
          return false
        }
        let typeArray = ref([
          {
            name: 'doc',
            type: 'application/msword'
          },
          {
            name: 'docx',
            type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
          },
          {
            name: 'xls',
            type: 'application/vnd.ms-excel'
          },
          {
            name: 'xlsx',
            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
          }
        ])
        // accept传入 .doc,.docx,.xlsx,.xls 匹配不上
        let fileType = file.type.split('/')[1]
        if (typeArray.value.find((i: any) => i.type === file.type)) {
          fileType = typeArray.value.find(
            (i: any) => i.type === file.type
          )!.name
        }

        return !props.accept.includes(fileType)
      },
      message: '文件格式不符合要求'
    },
    {
      validator: (file) => file.size > props.maxSize * 1024 * 1024,
      message: `文件不能超过${props.maxSize}MB`
    },
    ...props.rules
  ]

  const rule = rules.find((rule) => rule.validator(rawFile))
  if (rule) {
    ElMessage.warning(rule.message)
    return false
  }
}

const onChange = () => {
  if (props.removeFail) {
    fileList.value = fileList.value.filter((file) => file.status !== 'fail')
    console.log(fileList.value)
  } else {
    fileList.value = [...fileList.value]
  }
}

// 最大上传数量为一 上传覆盖上次文件
const handleExceed: UploadProps['onExceed'] = (files) => {
  uploadRef.value!.clearFiles()
  const file: any = files[0]
  file.uid = genFileId()
  uploadRef.value!.handleStart(file)
  fileList.value = []
  httpRequest({ file })
}

const httpRequest: ElUploadProps['httpRequest'] = (options: any) => {
  const { file } = options
  // 当前文件信息
  const fileListItem = reactive<UploadFile>({
    name: file.name,
    url: URL.createObjectURL(file),
    status: 'uploading',
    percentage: 0
  })

  fileList.value.push(fileListItem)

  // 进度条
  const { cancel } = useProgressFake(
    (percent) => {
      fileListItem.percentage = percent
    },
    {
      interval: 100,
      maxNum: 99,
      increaseNum: 3
    }
  )

  // 请求上传接口
  const formdata = new FormData()
  formdata.append(props.httpFileKey, file)

  return props
    .http(formdata)
    .then(({ data }) => {
      // fileListItem.name = data.name
      //joinBaseUrlFile()
      fileListItem.url = data
      fileListItem.status = 'success'
      fileListItem.response = data
      emit('currentHttp', fileListItem)
      if (props.showMessage) {
        ElMessage.success('上传成功')
      }
    })
    .catch(() => {
      fileListItem.status = 'fail'
      if (props.showMessage) {
        ElMessage.error('上传失败')
      }
    })
    .finally(cancel)
}

defineExpose({
  getUploadInstance: () => uploadRef.value
})
</script>

<style lang="scss" scoped>
.m-upload {
  width: 100%;
}
:deep(.el-upload-dragger) {
  padding: 15px 10px 15px 10px;
}
</style>
